#include "stdafx.h"
#include "LoginHelper.h"

#include "Poco/Format.h"
#include "Poco/SharedPtr.h"
#include "Poco/Exception.h"
#include "Poco/Data/Session.h"
#include "Poco/Data/SessionFactory.h"
#include "Poco/Data/Statement.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/Data/MySQL/MySQLException.h"
#include "Poco/Data/MySQL/Connector.h"
#include "Poco/UnicodeConverter.h"

#include "Logger.h"

namespace ExamSystem
{
    using Poco::SharedPtr;
    using Poco::Exception;
    using Poco::Data::Session;
    using Poco::Data::SessionFactory;
    using Poco::Data::Statement;
    using Poco::Data::RecordSet;
    using Poco::Data::now;
    using Poco::Data::MySQL::ConnectionException;

    // this class is only used to register mysql
    static bool flagMySqlRegistered = false;

    CLoginHelper::CLoginHelper(void)
    {
        if (!flagMySqlRegistered)
        {
            Poco::Data::MySQL::Connector::registerConnector();
            flagMySqlRegistered = true;
        }

        m_options["host"] = "localhost";
        m_options["port"] = "3306";
        m_options["username"] = "loginhelper";
        m_options["password"] = "loginhelper";
        m_options["database"] = "school";
        m_options["studentTable"] = "students";
        m_options["stuName"] = "name";
        m_options["stuNumber"] = "number";
        m_options["invigilatorTable"] = "invigilators";
        m_options["invUsername"] = "username";
        m_options["invPassword"] = "password";
        GenerateConnectionString();
    }

    CLoginHelper::~CLoginHelper(void)
    {

    }

    void CLoginHelper::SetProperty(const std::string &property, const std::string &value)
    {
        std::map<std::string, std::string>::iterator it = m_options.find(property);
        if (it == m_options.end())
        {
            systemLogger.warning("Unsupported property: " + property);
            return;
        }

        it->second = value;
        GenerateConnectionString();
    }

    void CLoginHelper::GenerateConnectionString(void)
    {
        m_connectionString = Poco::format(
            "host=%s;port=%s;user=%s;password=%s;db=%s;compress=true;auto-reconnect=true",
            m_options["host"], m_options["port"], m_options["username"],
            m_options["password"], m_options["database"]
        );
    }

    CLoginHelper::OperationResult CLoginHelper::StudentLogin(const std::string &name, const std::string &number)
    {
        // create a section and try to login
        SharedPtr<Session> session;
        try
        {
            session = new Session(SessionFactory::instance().create(Poco::Data::MySQL::Connector::KEY, m_connectionString));

            if (!session || !session->isConnected()) 
                return Error;

            Session &ses = *session;
            ses << "SET names utf8", now;

            Statement select(ses);
            select << Poco::format(
                "SELECT %s FROM %s WHERE %s='%s'",
                m_options["stuName"], m_options["studentTable"],
                m_options["stuNumber"], number
                );
            select.execute();

            RecordSet rs(select);
            if (rs.rowCount())
            {
                rs.moveFirst();
                std::string str = rs[0].convert<std::string>();
                if (rs[0].convert<std::string>() == name)
                    return Succeeded;
                else
                    return Failed;
            }
            else
                return Failed;
        }
        catch (Exception& exc)
        {
            systemLogger.error(exc.displayText());
            return Error;
        }
    }

    CLoginHelper::OperationResult CLoginHelper::InvigilatorLogin(const std::string &user, const std::string &password)
    {
        // create a section and try to login
        SharedPtr<Session> session;
        try
        {
            session = new Session(SessionFactory::instance().create(Poco::Data::MySQL::Connector::KEY, m_connectionString));

            if (!session || !session->isConnected()) 
                return Error;

            Session &ses = *session;
            ses << "SET names utf8", now;

            Statement select(ses);
            select << Poco::format(
                "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'",
                m_options["invUsername"], m_options["invigilatorTable"],
                m_options["invUsername"], user,
                m_options["invPassword"], password);

            select.execute();

            if (select.execute())
                return Succeeded;
            else
                return Failed;
        }
        catch (ConnectionException& exc)
        {
            systemLogger.error(exc.displayText());
            return Error;
        }
    }

}